package networkInterface;

import hci.ChatGui;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

import observerNotification.ErrorObserverNotification;

import main.ChatSystem;
import model.Datagram;
import cs.remoteapp.Message;


/**
 * Implements receiving of Message objects
 * 
 * <p>
 * <ul>
 * <li>Project: 	ChatSystemG3</li>
 * <li>Called by: 	ChatNI</li>
 * </ul>
 * </p>
 * 
 * @author COMBES Nicolas - LECHANTRE Arnaud
 * @see Runnable
 * @see ChatNIReceiver
 */
public class ChatNIMessageReceiver extends ChatNIReceiver{

	/**
	 * App controller (see MVC pattern)
	 */
	private FromNetwork chatNi;			
	/**
	 * Buffer used to receive the messages
	 */
	private byte[] buffer;							
	/**
	 * Receiving socket
	 */
	private DatagramSocket socket;					
	/**
	 * Packet that will host (contain) incoming messages
	 */
	private DatagramPacket packet;					
	/**
	 * Incoming data (saving incoming stream in here for better performance)
	 */
	private ByteArrayInputStream inputStream;		
	/**
	 * Incoming object stream
	 */
	private ObjectInputStream  objectStream;		
	
	
	/**
	 * construct; UDP socket is created using static dupPort from main.ChatSystem class
	 * @param chatNI concerned interface
	 * @throws SocketException
	 */
	public ChatNIMessageReceiver(FromNetwork chatNI) throws SocketException {
		super(chatNI);
		
		//We first save the boundary of the package in order to be able to inform the controller when a message is received.
		this.chatNi=chatNI;	
		
		//We first initialize the buffer (with a huge size in order to be sure not to have problems )
		this.buffer = new byte[5000]; 	
		if (ChatSystem.debugMode) {System.out.println("networkInterface.ChatNIReceiver : oppening");}
		
		//Then we try to create the socket.
		this.socket = new DatagramSocket(ChatSystem.udpPort);		
		
		//We initialize the packet
		this.packet = new DatagramPacket(buffer, buffer.length); 
	}

	
	/**
	 * Starts a "listenner" thread
	 * @see Runnable
	 */
	@Override
	public void run() {
		
		if (ChatSystem.debugMode) {System.out.println("networkInterface.ChatNIReceiver : Running NI Receiver");}
		
		String ch;					//String used for displaying informations about the received packet into the console
		Message receivedMess;		//Will contain the message received from the network
		
		
		while(!super.hasToClose()) {  //while the user does not ask to close the thread
			try {
				//First we initialize (again) the input stream (in order to clean it).
				this.inputStream= new ByteArrayInputStream(buffer);
				
				//Then we try the packet reception
				if (ChatSystem.debugMode && this.socket == null) System.out.println("networkInterface.ChatNIReceiver : this.socket est null");
				this.socket.receive(this.packet);
				
				if(buffer.length!=0){
					//If we arrive there, a message has been received (otherwise go to catch )
					//We create an output stream to get the message from the stream
					objectStream = new ObjectInputStream(inputStream);
					
					//if we are in debug mode, we display the packet to the user and his sender
					if(ChatSystem.debugMode){
						ch =this.packet.toString(); 
						System.out.println("networkInterface.ChatNIReceiver : Paquet recu : message = " + ch + 
								" - envoyeur = " + this.packet.getAddress().getHostName() ); 
					}
					
					try {
						//Then we create the message from the received string
						receivedMess = (Message) objectStream.readObject();
						//And prevent the controller that a new message is received
						if (ChatSystem.testOnSingleComputer || !this.packet.getAddress().getHostAddress().equals(InetAddress.getLocalHost().getHostAddress())) {
							//We ask the package boundary to transmit the message to the controller
							this.chatNi.receiveUDPDatagram(new Datagram(this.packet.getAddress(),receivedMess) );
						}
						
					} catch (IOException e) {
						if(ChatSystem.debugMode)System.out.println("networkInterface.ChatNIReceiver : Error in message reception");
					} catch (ClassNotFoundException e) {
						if(ChatSystem.debugMode)System.out.println("networkInterface.ChatNIReceiver : Received message not recognized");
					} 
				} 
			} catch (IOException e1) {
				if(ChatSystem.debugMode)System.out.println("networkInterface.ChatNIReceiver : Error input stream creation");
				if(!this.hasToClose())this.chatNi.relayError(new ErrorObserverNotification(ChatGui.strings.getString("errorSocketAlreadyOpenedText")));
			}
		}

		if(ChatSystem.debugMode)System.out.println("networkInterface.ChatNIReceiver : Receiver stoped");

		this.socket.close();
		if(ChatSystem.debugMode)System.out.println("networkInterface.ChatNIReceiver : Reception socket closed");
	}
	
	
	/**
	 * Closes socket
	 * @see ChatNIReceiver
	 */
	@Override
	public void close() {
		super.close();
		this.socket.close();
	}
	

}
